'''
基于主程序，去除湿传递的影响
'''

from before_compute import *  # 计算前处理
from plot import *  # 可视化处理
from load_plot_coutour import *  # 输出等高线图
from computing import *  # 迭代计算模块
from transmittance import *  # 传递系数模块
import time

# 创建文件夹、复制参数文件至创建的文件夹中，返回文件夹名
def mkdir_cppar():
    from tkinter.filedialog import askdirectory
    import shutil
    print('请选择保存数据的文件夹：')
    folderName = askdirectory()
    if folderName == '':  # 若未选择文件夹，保存到临时目录中
        folderName = 'temp/data_out'
        makedirs(folderName)
    shutil.copy('template/inputdata/parameter.xlsx ',
                '{}/parameter.xlsx'.format(folderName))  # 复制输入的参数文件
    return folderName


def heat_compute_loop(loop_count, x_step, time_step, max_error, T, s_coe_all, boundary_out, boundary_in):
    # 数据取值（分别为密度，导热系数，比热容，平衡曲线系数abc，传质扩散系数def，材料类型[0表示空气]）
    [den_m, conductivity_m, capacity_m, a, b, c, d, e, f, m_type] = s_coe_all
    [T_out,  W_out,  i_out, v_out, r_out] = boundary_out
    [T_in, W_in, i_in, v_in, r_in] = boundary_in

    # 初始化温湿度源项
    x_grid = len(s_coe_all[0])
    sp_T = np.zeros(x_grid, dtype=float)  # 创建源项（温度场）
    # 保留上一时刻的温度场用于下一时刻的计算
    T0 = np.array(T, dtype=float)

    # 边界条件读取完毕，进行迭代计算
    # 计算下一时刻的温湿度分布
    for j in range(int(loop_count)):
        # 1.迭代计算保留上一次的计算值，用于收敛判断
        T_last = np.array(T)

        # 2.计算温度场
        # 2.1 更新温度场计算系数a,b
        a_T = den_m * capacity_m
        b_T = conductivity_m
        # 2.2 更新温度场源项sp
        # 2.2.1 室外侧（左侧，x=0处）源项计算
        h_out = 8
        q_out = q_convection(h_out, T_out, T[0]) + i_out * 0.6
        sp_T[0] = q_out
        # 2.2.2 室内侧（右侧，x=L处）源项计算
        h_in = 3
        q_in = q_convection(h_in, T_in, T[-1]) + i_in * 0.6
        sp_T[-1] = q_in

        # 2.2.3 墙体内部源项计算
        # ——————待完善——————
        # 2.3 温度场计算
        def heat_final_coefficient(Ap0, x0, sp):
            coefficient = certain_coefficient(Ap0, x0)
            coefficient += source_coefficient(sp)
            return coefficient

        def heat_compute(ap, bp, x_step, time_step, x0, sp):
            AW = AW_coefficient(bp, x_step)
            AE = AE_coefficient(bp, x_step)
            Ap0 = Ap0_coefficient(ap, x_step, time_step)
            AP = AP_coefficient(Ap0, AW, AE)
            D = heat_final_coefficient(Ap0, x0, sp)
            outputs = tdma(AW, AP, AE, D)
            return outputs

        T = heat_compute(a_T, b_T, x_step, time_step, T0, sp_T)

        # 4.循环校验
        error = np.max(np.abs(T - T_last)) / np.max(T)  # 计算温度场最大的相对误差

        if error < max_error:  # 判断误差是否合理，若误差小于最大误差限，跳出内循环
            # print('迭代次数：%d次；误差：%.2e ' % (j + 1, error))
            break  # 停止本次运算，进入下一时刻的温湿度计算

        # 循环过程中的异常处理，还有其它异常情况需要处理--------------------------
        elif np.isnan(error):
            print('NAN异常，迭代出现发散，准备调整时间步长')
            raise RuntimeError
        elif j == loop_count - 1:  # 输出达到最大设置循环次数时，进行子循环计算
            print('最大次数异常，步长/10，进入子循环')
            T = compute_loop_max(loop_count, x_step, time_step, max_error, T0, s_coe_all,
                                    boundary_out, boundary_in)

    return T, sp_T[0], sp_T[-1]


# 当运行过程中，出现迭代次数达到最大时，仍未达到要求误差，分次进行计算
def compute_loop_max(loop_count, x_step, time_step, max_error, T, s_coe_all, boundary_out, boundary_in):
    n = 10
    time_step /= n
    for k in range(n):
        # print('正在进行第%d次子循环：' % (k + 1), end='')
        T, q_out, q_in = heat_compute_loop(loop_count, x_step, time_step, max_error, T,
                                           s_coe_all, boundary_out, boundary_in)
        # print('第%d次子循环完毕！' % (k + 1))
    return T


# 带入计算的边界条件的选择
def number_of_boundary(second_computing, boundary, time_interval=3600):
    weather_data_count = len(boundary[0])  # 气候数据数量等于读取边界条件的长度
    n = int(second_computing // time_interval)  # 得到正在计算的时间段（第n个）
    n = n % weather_data_count  # 取余，进行整体的循环（当计算总时间大于边界条件总时间，从头循环）
    return n


def heat_start():
    # 1.读取计算参数
    time_step, time_count, loop_count, max_error, \
    s_coe_all, x_step, time_now, T, W, boundary_out, boundary_in = load_parameters('xlsx')
    time_total = time_step * time_count  # 计算模拟时间长度，用于进度表的显示

    # 2.设置储存数据的二维数据（行叠加），将初始温湿度场数据储存起来，增加时间项，增加内外传热传质量
    data_T = np.hstack((time_now, T, 0, 0))

    # 3.开始循环计算
    print('*********程序开始计算***********')
    start = time.clock()  # 开始计时
    time_counter = start
    fresh_time = 1
    # 外循环：时间循环
    for i in range(int(time_count)):
        # 3.1 迭代计算前的数据处理
        second = (i + 1) * time_step + time_now  # 得到正在计算的时刻
        # 室内外边界条件的选择
        n_out = number_of_boundary(second, boundary_out, 3600)
        n_in = number_of_boundary(second, boundary_in, 3600)

        # 3.2 核心计算部分，通过compute_loop函数进行迭代计算
        # 输入参数主要为：上一时刻温度场，上一时刻湿度场，墙体材料参数，i时刻的边界条件
        T, q_out, q_in = heat_compute_loop(loop_count, x_step, time_step, max_error, T,
                                           s_coe_all, boundary_out[1:, n_out], boundary_in[1:, n_in])

        # 3.3 显示整体的计算进度
        if time.clock() - time_counter > fresh_time:  # 每隔fresh_time ，打印一次
            progress = (i + 1) / time_count * 100  # 计算整体进度%
            compute_rate = (second - time_now) / time.clock()  # 定义计算速率，提示计算时间
            remaining = (time_total + time_now - second) // compute_rate  # 计算剩余的时间

            print('正在计算时刻：%s' % second2date(second))  # 输出正在计算的时刻
            print('程序总进度：%.1f%%' % progress)  # 显示整体进度%
            print('计算速率为：%.2f' % compute_rate)  # 显示计算的速率
            print('*' * 20 + '预计还需要计算{}'.format(second2date(remaining)) +
                  '*' * 20 + '已运行%s' % second2date(int(time.clock())))  # 显示剩余的时间

            time_counter = time.clock()  # 更新计数时间

        # 3.4 储存数据
        # 判断1：总时间大于24小时，每小时储存一次
        judge_1 = (time_total > 24 * 3600) and (second % 3600 == 0)
        # 判断2：总时间小于24小时大于12小时，每二十分钟储存一次
        judge_2 = (24 * 3600 >= time_total > 12 * 3600) and (second % 1200 == 0)
        # 判断3：总时间小于12小时大于1小时，每五分钟储存一次
        judge_3 = (12 * 3600 >= time_total > 1 * 3600) and (second % 300 == 0)
        # 判断4：总时间小于1小时大于0.5小时，每分钟储存一次
        judge_4 = (1 * 3600 >= time_total > 0.5 * 3600) and (second % 60 == 0)
        # 判断5：总时间小于0.5小时，都储存
        judge_5 = (0.5 * 3600 >= time_total)
        # 综合
        judge = judge_1 or judge_2 or judge_3 or judge_4 or judge_5
        # 上述条件成立时，储存数据
        if judge:
            data_T = np.row_stack([data_T, np.hstack((second, T, q_out, q_in))])

    # 3.4 主体计算完毕，计时
    end = time.clock()
    t_compute = end - start
    print('计算程序完成，计算总时长：', second2date(t_compute))

    return data_T, t_compute, x_step


def savedata(folderName, data_T, t_compute, x_step):
    # 保存计算所花的时间
    np.savetxt('{}/{}.txt'.format(folderName, 'computed_time'),
               [second2date(t_compute), t_compute], fmt='%s', delimiter=',')
    # 4.输出并保存数据
    makedirs(folderName)
    # 4.1 温湿度场计算
    T_total = data_T[:, :-2]
    # 温湿度转化：绝对温度转化为摄氏温度，湿度转化为百分数
    T_total[:, 1:] -= 273.15
    title = np.hstack((t_compute, x_step))  # 表头：包括总计算时间，及各段网格大小
    T_total = np.row_stack([title, T_total])
    # 4.2 传热传质量数据
    q_total = np.hstack((data_T[:, :1], data_T[:, -2:]))

    # 4.3 保存成文件
    # 保存的数据整理
    fileName = ['温度场', '传热量']
    unit = ['℃', 'W/m2']
    data = [T_total, q_total]

    print('正在保存数据...')
    for n in range(2):
        np.savetxt('{}/data_{}.csv'.format(folderName, fileName[n]),
                   data[n], fmt='%.12f', delimiter=',')
    print('数据保存完成！')

    # 4.3 数据可视化
    # 4.3.1 保存曲线图
    print('正在输出定性曲线图表......')
    fig_curve_T = plot_chart(T_total, '温度场', '℃')  # 输出温度场图表

    print('********输出图表完成！**********')
    fig = fig_curve_T
    # 保存图表图片
    fig.savefig('{}/curve_{}.png'.format(folderName, '温度场'))
    # 4.4.2 保存等高线图
    field_style = ['t', 'rh', 'p']
    layers_thickness = readExcel('{}/parameter.xlsx'.format(folderName), 'wall')[1][1:-1]
    for n in range(1):
        filename = '{}/data_{}.csv'.format(folderName, fileName[n])
        style = field_style[n]
        plot_field_contour(filename, style, layers_thickness)
        plt.savefig('{}/contour_{}.png'.format(folderName, fileName[n]), dpi=350)


if __name__ == '__main__':
    print('子程序（热传递模拟）开始：')
    folderName = mkdir_cppar()  # 创建文件夹，复制计算参数，返回文件夹路径
    data_T, t_compute, x_step = heat_start()  # 开始计算，返回相关参数用于数据保存用
    savedata(folderName, data_T, t_compute, x_step)  # 保存数据
    print('程序结束！')
